#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

int fd_lcd;
int *map_lcd;

int lcd_init()
{
	//打开LCD的设备文件
	fd_lcd = open("/dev/fb0",O_RDWR);
	if(-1 == fd_lcd)
	{
		perror("open LCD failed:");
		return -1;
	}
	
	//映射LCD
    //成功的时候返回申请映射空间的地址
	map_lcd = (int *)mmap(	NULL,//申请映射空间的指定地址，一般可填NULL，表示由系统自动分配
							800*480*4,//申请的映射空间的大小，单位字节
							PROT_READ|PROT_WRITE,//映射空间的属性，读写属性
							MAP_SHARED,//映射空间的属性，是否共享
							fd_lcd,//目标文件的文件描述符
							0);//映射空间的地址偏移量表示，0不偏移
	if(map_lcd == MAP_FAILED)
	{
		perror("map LCD failed:");
		return -1;
	}
}

int lcd_uinit()
{
	//解除映射
	munmap(map_lcd,800*480*4);
	//关闭LCD的设备文件
	close(fd_lcd);
}

/*
x_s，y_s定位输出图片位置
*/
int lcd_draw_bmp(int x_s,int y_s,char *pathname)
{
	//打开图片文件
	int fd_bmp;
	fd_bmp = open(pathname,O_RDWR);
	if(-1 == fd_bmp)
	{
		perror("open bmp failed:");
		return -1;
	}
	
	//读取文件的54字节数据信息，里面有图片的宽和高的信息
	char buf_head[54];
	read(fd_bmp,buf_head,sizeof(buf_head));
	
	//计算图片的宽度和高度
	int bmp_w,bmp_h;
	//19~22为宽，23~26为高；这个bmp是24位的
    //通过左移把四个一字节合并成一个四字节
	bmp_w = buf_head[18] | buf_head[19]<<8 | buf_head[20]<<16 | buf_head[21]<<24;
	bmp_h = buf_head[22] | buf_head[23]<<8 | buf_head[24]<<16 | buf_head[25]<<24;
	printf("bmp_w=%d,bmp_h=%d\n",bmp_w,bmp_h);
	 
	//计算被补充的无效字节的大小
	int n;
	if(bmp_w*3%4)
	{
		n = 4-bmp_w*3%4;
	}else{
		n = 0;
	}
	printf("n=%d\n",n);
	
	//读取图片文件里的颜色数据
	char buf_bmp[bmp_w*bmp_h*3];
	int i;
	
	for(i=0;i<bmp_h;i++)
	{
        //一行一行读,然后调整光标到下一行
		read(fd_bmp,&buf_bmp[bmp_w*3*i],bmp_w*3);
		lseek(fd_bmp,n,SEEK_CUR);
	}
	
	//关闭图片文件
	close(fd_bmp);
	
	//对读取的bmp的颜色数据进行ARGB整合
	int buf_lcd[bmp_w*bmp_h];
	for(i=0;i<bmp_w*bmp_h;i++)
	{
		//因为bmp是BGR格式
		//ARGB			A			R				G				B
		buf_lcd[i] = 0x00<<24 | buf_bmp[i*3+2]<<16 | buf_bmp[i*3+1]<<8 | buf_bmp[i*3];
	}
	
	int x,y;
	
	int buf_lcd_swap[bmp_h][bmp_w];
	
	for(y=0;y<bmp_h;y++)
	{
		for(x=0;x<bmp_w;x++)
		{
			//因为屏幕的一行对应着BMP格式图片的最后一行,所以要倒过来
			buf_lcd_swap[y][x] = buf_lcd[(bmp_h-1-y)*bmp_w+x];	
		}
	}
	
	//若x_s为-1，则计算x轴的居中显示位置，否则按照实际数值使用
	if(x_s == -1)
	{
		x_s = (800-bmp_w)/2;
	}
	
	//若y_s为-1，则计算y轴的居中显示位置，否则按照实际数值使用
	if(y_s == -1)
	{
		y_s = (480-bmp_h)/2;
	}

	//1,就右下角
	if(x_s==1){
		x_s = 800-bmp_w;
	}
	if(y_s==1){
		y_s = 480-bmp_h;
	}
	
    //输出到屏幕
	for(y=0;y<bmp_h;y++)
	{
		for(x=0;x<bmp_w;x++)
		{
            //取址赋值
			*(map_lcd+(y+y_s)*800+x+x_s) = buf_lcd_swap[y][x];	
		}
	}
}

int main()
{
	lcd_init();
	//1.bmp分辨率是511*311
	lcd_draw_bmp(-1,-1,"./picture/1.bmp");
	
	sleep(2);
	
	lcd_draw_bmp(1,1,"./picture/1.bmp");
	
	lcd_uinit();
	
	return 0;
}
